#include <dspSimulator/instruction/addkpc.h>

bool ADDKPC::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    return checkStandardCondition(dspMachine);
}

std::shared_ptr<Instruction> ADDKPC::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<ADDKPC> addkpc;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (existStandardCondition(data) && checkBits<11>(data, 2, 0b00001011000)) {
            addkpc = std::make_shared<ADDKPC>(dspInstInfo->getAddress(), baseCycle);
            addkpc->creg_z = extractBits<4>(data, 28);
            addkpc->dst = extractBits<5>(data, 23).to_ulong();
            addkpc->cst = extractBits<7>(data, 16).to_ulong();
            addkpc->auxCst = extractBits<3>(data, 13).to_ulong();
            addkpc->s = data[1];
            addkpc->funcUnit = FuncUnit::S2;
            addkpc->crhi = 0b00000;
            addkpc->crlo = 0b10000;
            addkpc->numOfInsertNOP = addkpc->auxCst;
            addkpc->p = data[0];
            addkpc->instFormat = InstFormat::Kind1;
        }
    }
    return addkpc;
}

void ADDKPC::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                               std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);
    // --------------------------------- ADDKPC (.unit) scst7, dst, ucst3 ---------------------------------------------
    {
        llvm::Function *addkpc = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "ADDKPCKind1",
                                                         module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", addkpc);
        irBuilder->SetInsertPoint(entryBB);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);

        auto pce1Ptr = getCRegPtr(module, irBuilder);
        auto pce1 = irBuilder->CreateLoad(pce1Ptr);

        auto cst7 = getCstN(module, irBuilder, 7);
        auto scst7 = irBuilder->CreateSExt(cst7, irBuilder->getInt32Ty());
        auto lhsVal = irBuilder->CreateShl(scst7, irBuilder->getInt32(2));

        auto newPC = irBuilder->CreateAdd(pce1, lhsVal);
        irBuilder->CreateStore(newPC, dstRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::string ADDKPC::toString() const {
    return getStandardConditionString() + " " + m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " " +
           dec2hex(extractBitsAndToInt<7>(cst)) + "," + getDstReg32Name() + "," + std::to_string(auxCst);
}

void ADDKPC::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {

}

std::shared_ptr<DspInstruction> ADDKPC::clone() {
    return std::make_shared<ADDKPC>(*this);
}
